﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Threading;

namespace Pfz.Collections
{
	/// <summary>
	/// Class that enumerates an ActualValueCollection.
	/// If three new values appears and you call GetNext only once, you
	/// will only receive the last value. If you call GetNext when there is no
	/// value, your thread will be blocked waiting for a new value.
	/// </summary>
	public sealed class ActualValueEnumerator<T>:
		IFastEnumerator<T>,
		IEnumerator<T>
	where
		T: class
	{
		private readonly ActualValueCollection<T> _collection;
		private T _lastValue;
		
		internal ActualValueEnumerator(ActualValueCollection<T> collection)
		{
			_collection = collection;
		}
		
		#region IDisposable
			// do nothing. The dispose should be called only by the thread
			// who created this, so it is not needed to free a thread.
			void IDisposable.Dispose()
			{
			}
			bool IAdvancedDisposable.WasDisposed
			{
				get
				{
					return false;
				}
			}
		#endregion
		
		/// <summary>
		/// Gets the next value in the collection. A return of null means
		/// the collection ended.
		/// </summary>
		public T GetNext()
		{
			lock(_collection._lock)
			{
				while(true)
				{
					if (_collection.WasDisposed)
					{
						_lastValue = null;
						return null;
					}
					
					var value = _collection.Value;
					if (value != _lastValue)
					{
						_lastValue = value;
						return value;
					}
					
					Monitor.Wait(_collection._lock);
				}
			}
		}

		/// <summary>
		/// Gets the last read value.
		/// </summary>
		public T Current
		{
			get 
			{
				return _lastValue;
			}
		}

		/// <summary>
		/// Tries to get a new value and store it in the Current property.
		/// </summary>
		public bool MoveNext()
		{
			return GetNext() != null;
		}

		#region IEnumerator Membres
			object IEnumerator.Current
			{
				get
				{
					return _lastValue;
				}
			}

			void IEnumerator.Reset()
			{
				throw new NotSupportedException();
			}
		#endregion
	}
}
